import ast
import base64
import datetime
import json
import os
import re
import threading
from datetime import timedelta, date

import yaml
from django.http import QueryDict
from django.shortcuts import redirect
from kubernetes import config, client
from kubernetes.client import ApiException
from kubernetes.stream import stream


def auth_check(auth_type, auth_value):
    if auth_type == 'token':
        token = auth_value
        configuration = client.Configuration()
        configuration.host = 'https://kubernetes.default.svc.cluster.local'
        configuration.verify_ssl = False
        # configuration.ssl_ca_cert = "~/myCode/k8sDashboard/ca.crt"
        configuration.api_key['authorization'] = token
        configuration.api_key_prefix['authorization'] = 'Bearer'
        client.Configuration.set_default(configuration)
        try:
            core_api = client.CoreApi()
            core_api.get_api_versions()
            return True
        except Exception:
            return False
    elif auth_type == 'kubeconfig':
        file_path = os.path.join('config', auth_value)
        config.load_kube_config(file_path)
        try:
            core_api = client.CoreApi()
            core_api.get_api_versions()
            return True
        except Exception:
            return False


def self_login_required(func):
    def inner(request, *args, **kwargs):
        is_login = request.session.get('is_login', False)
        if is_login:
            return func(request, *args, **kwargs)
        else:
            return redirect("/login")

    return inner


def dt_format(timestamp):
    t = date.strftime((timestamp + timedelta(hours=8)), '%Y-%m-%d %H:%M:%S')
    return t


def memory_format(memory):
    memory = str(memory)
    # 将memory后缀去除，改为浮点数，方便运算
    if 'K' in memory:
        m = float(re.match(r'[0-9.]{0,20}', memory).group())
        memory_mb = round(m / 1024 / 1024, 2)
    elif 'M' in memory:
        m = float(re.match(r'[0-9.]{0,20}', memory).group())
        memory_mb = round(m / 1024, 2)
    elif 'G' in memory:
        m = float(re.match(r'[0-9.]{0,20}', memory).group())
        memory_mb = round(m, 2)
    else:
        m = float(memory)
        memory_mb = round(m, 2)
    return memory_mb


def cpu_format(cpu):
    # 将cpu后缀去除，改为浮点数，方便运算
    cpu = str(cpu)
    if 'm' in cpu:
        c = float(re.match(r'[0-9.]{0,20}', cpu).group())
        cpu_f = round(c / 1000, 2)
    else:
        c = float(re.match(r'[0-9.]{0,20}', cpu).group())
        cpu_f = round(c, 2)
    return cpu_f


class K8sApi:
    def __init__(self, request, request_type='http'):
        """
        因为有两种类型的链接，获取session方式不同，所以需要指定下，默认为http
        request_type http or websocket
        """
        self.request = request
        if request_type == 'websocket':
            auth_value = self.request['session']['token']
            auth_type = self.request['session']['auth_type']
        else:
            auth_type = request.session.get('auth_type')
            auth_value = request.session.get('token')
            self.search_key = self.request.GET.get('search_key')
        self.load_auth_config(auth_type, auth_value)

    def load_auth_config(self, auth_type, auth_value):
        auth_check(auth_type, auth_value)

    def paging(self, content):
        if self.request.GET.get('page'):
            # 如果是分页请求，则分页处理
            page = int(self.request.GET.get('page', 1))
            limit = int(self.request.GET.get('limit'))
            start = (page - 1) * limit
            end = page * limit
            return content[start:end]
        else:
            return content

    def error_check(self, e, method='get'):
        print('error check :%s' % e)
        try:
            error_body = ast.literal_eval(e.body)
            code = error_body['code']
            res = {'code': code, 'msg': error_body['message']}
        except Exception:
            res = {'code': 1, 'msg': str(e)}
        return res

    def set_limit(self, limit):

        if limit == "0.5C1G":
            limit = client.V1ResourceRequirements(limits={"cpu": "0.5", "memory": "1Gi"},
                                                  requests={"cpu": "0.2", "memory": "0.5Gi"})
        elif limit == "1C2G":
            limit = client.V1ResourceRequirements(limits={"cpu": "1", "memory": "2Gi"},
                                                  requests={"cpu": "0.2", "memory": "0.5Gi"})
        elif limit == "2C4G":
            limit = client.V1ResourceRequirements(limits={"cpu": "2", "memory": "4Gi"},
                                                  requests={"cpu": "0.2", "memory": "0.5Gi"})
        elif limit == "4C4G":
            limit = client.V1ResourceRequirements(limits={"cpu": "4", "memory": "4Gi"},
                                                  requests={"cpu": "0.2", "memory": "0.5Gi"})
        else:
            limit = client.V1ResourceRequirements(limits={"cpu": '0.5', "memory": "1Gi"},
                                                  requests={"cpu": '0.2', "memory": "0.5Gi"})
        return limit

    def read_resource(self, api_name, resource, name, namespace):
        other_list = ['namespace', 'node', 'persistent_volume']
        if resource in other_list:
            resource_api = api_name + '.read_' + resource + '(name=\'' + name + '\', _preload_content=False).read() '
        else:
            resource_api = api_name + '.read_namespaced_' + resource + '(name=\'' + name + '\', namespace=\'' + namespace + '\', _preload_content=False).read() '
        try:
            data = eval(resource_api)
            data = data.decode()
            data = yaml.safe_dump(json.loads(data))
            code = 0
            msg = "获取数据成功"
        except Exception as e:
            return self.error_check(e)
        return {'code': code, 'msg': msg, 'data': data}

    def labels_handle(self, labels):
        # 标签处理
        _labels = {}
        try:
            for label in labels:
                k = label.split("=")[0]
                v = label.split("=")[1]
                _labels[k] = v
        except IndexError:
            res = {"code": 1, "msg": "标签格式错误，请修改！"}
            return res
        return _labels


class NamespaceApi(K8sApi):

    def __init__(self, request):
        super().__init__(request)
        self.core_api = client.CoreV1Api()

    def get(self):
        contents = []
        try:
            for content in self.core_api.list_namespace().items:
                name = content.metadata.name
                labels = content.metadata.labels
                create_time = dt_format(content.metadata.creation_timestamp)
                status = content.status
                namespace = {'name': name, 'labels': labels, 'create_time': create_time, 'status': status.phase}
                # 模糊搜索
                if self.search_key:
                    if self.search_key in name:
                        contents.append(namespace)
                else:
                    contents.append(namespace)
        except Exception as e:
            return self.error_check(e)
        count = len(contents)
        contents = self.paging(contents)
        code = 0
        msg = '获取namespace成功'
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        try:
            self.core_api.delete_namespace(name)
            code = 0
            msg = '删除namespace成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        name = self.request.POST['name']
        body = {
            "apiVersion": "v1",
            "kind": "Namespace",
            "metadata": {"name": name},
        }
        try:
            self.core_api.create_namespace(body=body)
            code = 0
            msg = '创建namespace成功！'
            res = {'code': code, 'msg': msg}
            return res
        except ApiException as e:
            return self.error_check(e, method='post')


class NodeApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.core_api = client.CoreV1Api()

    def get(self):
        contents = []
        try:
            now_time = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
            for content in self.core_api.list_node().items:
                name = content.metadata.name
                labels = content.metadata.labels
                create_time = dt_format(content.metadata.creation_timestamp)
                heartbeat_time = content.status.conditions[-1].last_heartbeat_time.strftime("%Y-%m-%d %H:%M:%S")
                differ_time = (datetime.datetime.strptime(now_time, "%Y-%m-%d %H:%M:%S") - datetime.datetime.strptime(
                    heartbeat_time, "%Y-%m-%d %H:%M:%S")).seconds
                if differ_time > 120:
                    status = 'NotReady'
                else:
                    status = content.status.conditions[-1].type
                ip = content.status.addresses[0].address
                hostname = content.status.addresses[1].address
                cpu = content.status.allocatable['cpu']
                memory = memory_format(content.status.allocatable['memory'])
                capacity_cpu = content.status.capacity['cpu']
                capacity_memory = memory_format(content.status.capacity['memory'])
                pods = content.status.capacity['pods']
                node_info = content.status.node_info
                ready = ('是' if content.spec.unschedulable is None else '否')
                node = {
                    'name': name,
                    'labels': labels,
                    'create_time': create_time,
                    'status': status,
                    'kubelet_version': node_info.kubelet_version,
                    'CRI': node_info.container_runtime_version,
                    'ready': ready,
                    'cpu': cpu,
                    'memory': memory,
                    'capacity_cpu': capacity_cpu,
                    'capacity_memory': capacity_memory,
                    'ip': ip,
                    'hostname': hostname,
                    'pods': pods,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(node)
                else:
                    contents.append(node)
        except Exception as e:
            return self.error_check(e)

        count = len(contents)
        contents = self.paging(contents)
        code = 0
        msg = 'node数据获取成功'
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res


class PersistenVolumesApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.core_api = client.CoreV1Api()

    def get(self):
        contents = []
        try:
            for content in self.core_api.list_persistent_volume_with_http_info()[0].items:
                name = content.metadata.name
                labels = content.metadata.labels
                create_time = dt_format(content.metadata.creation_timestamp)
                status = content.status.phase
                access_modes = content.spec.access_modes
                storage_class_name = content.spec.storage_class_name
                persistent_volume_reclaim_policy = content.spec.persistent_volume_reclaim_policy
                capacity = content.spec.capacity['storage']
                data = {
                    'name': name,
                    'labels': labels,
                    'create_time': create_time,
                    'status': status,
                    'access_modes': access_modes,
                    'capacity': capacity,
                    'storage_class_name': storage_class_name,
                    'persistent_volume_reclaim_policy': persistent_volume_reclaim_policy,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        count = len(contents)
        contents = self.paging(contents)
        code = 0
        msg = '数据获取成功'
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        try:
            self.core_api.delete_persistent_volume(name)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        # name = self.request.POST['name']
        name = self.request.POST['volume_name']
        access_model = self.request.POST['access_model']
        capacity = self.request.POST['capacity'] + 'Gi'
        modules = self.request.POST['modules']
        plugins = self.request.POST['plugins']
        path = self.request.POST['local_dir']
        if plugins == 'host_path':
            body = client.V1PersistentVolume(
                api_version="v1",
                kind="PersistentVolume",
                metadata=client.V1ObjectMeta(name=name),
                spec=client.V1PersistentVolumeSpec(
                    capacity={'storage': capacity},
                    access_modes=[access_model],
                    host_path=client.V1HostPathVolumeSource(
                        path=path,
                        type=modules
                    )
                )
            )
        else:
            return {'code': 1, 'msg': '不支持的plugins类型：%s' % plugins}
        try:
            self.core_api.create_persistent_volume(body)
            code = 0
            msg = '创建pv成功！'
            res = {'code': code, 'msg': msg}
            return res
        except ApiException as e:
            return self.error_check(e, method='post')


class DeploymentApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.app_api = client.AppsV1Api()

    def get(self):
        namespace = self.request.GET.get('namespace')
        if namespace == 'all' or namespace is None:
            app_data = self.app_api.list_deployment_for_all_namespaces_with_http_info()[0].items
        else:
            app_data = self.app_api.list_namespaced_deployment(namespace).items
        contents = []
        try:
            for content in app_data:
                name = content.metadata.name
                namespace = content.metadata.namespace
                labels = content.metadata.labels
                create_time = dt_format(content.metadata.creation_timestamp)
                if content.status.ready_replicas is None:
                    ready_replicas = 0
                else:
                    ready_replicas = content.status.ready_replicas
                if content.status.replicas is None:
                    replicas = 0
                else:
                    replicas = content.status.replicas
                containers = content.spec.template.spec.containers[0].name
                data = {
                    'namespace': namespace,
                    'name': name,
                    'labels': labels,
                    'create_time': create_time,
                    'replicas': replicas,
                    'ready_replicas': ready_replicas,
                    'containers': containers,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.app_api.delete_namespaced_deployment(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        name = self.request.POST['name']
        namespace = self.request.POST['deploy_namespace']
        # 检查deployment是否存在
        for app in self.app_api.list_namespaced_deployment(namespace=namespace).items:
            if name == app.metadata.name:
                return {'code': 1, 'msg': 'deployment is exits'}

        image = self.request.POST['image-name']
        replica = int(self.request.POST['replica'])
        limit = self.request.POST['limit']
        # 标签处理
        labels = self.labels_handle(self.request.POST['labels'].split(","))
        limit = self.set_limit(limit)

        body = client.V1Deployment(
            api_version="apps/v1",
            kind="Deployment",
            metadata=client.V1ObjectMeta(name=name),
            spec=client.V1DeploymentSpec(
                replicas=replica,
                selector={'matchLabels': labels},
                template=client.V1PodTemplateSpec(
                    metadata=client.V1ObjectMeta(labels=labels),
                    spec=client.V1PodSpec(
                        containers=[client.V1Container(
                            name=name,
                            image=image,
                            resources=limit,
                        )]
                    )
                )
            )
        )
        try:
            self.app_api.create_namespaced_daemon_set(namespace=namespace, body=body)
            code = 0
            msg = "创建成功"
        except Exception as e:
            return self.error_check(e)

        return {'code': code, 'msg': msg}

    def put(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        body = self.app_api.read_namespaced_deployment(namespace=namespace, name=name)
        try:
            replicas = int(request_data['replicas'])
        except ValueError as e:
            return self.error_check(e)
        old_replicas = body.spec.replicas
        max_replicas = 15
        code = 0
        try:
            if old_replicas < replicas < max_replicas:
                body.spec.replicas = replicas
                self.app_api.patch_namespaced_deployment(name=name, namespace=namespace, body=body)
                msg = '扩容成功'
            elif replicas < old_replicas:
                body.spec.replicas = replicas
                self.app_api.patch_namespaced_deployment(name=name, namespace=namespace, body=body)
                msg = '缩容成功'
            elif replicas > max_replicas:
                msg = '副本数最大限制为' + str(max_replicas) + '请不要超过此限制!'
                code = 1
            elif replicas == old_replicas:
                msg = '副本数没有变化，不需要扩缩容'
        except Exception as e:
            return self.error_check(e)

        return {'code': code, 'msg': msg}


class DaemonsetApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.app_api = client.AppsV1Api()

    def get(self):
        contents = []
        namespace = self.request.GET.get('namespace')
        if namespace == 'all' or namespace is None:
            self.app_data = self.app_api.list_daemon_set_for_all_namespaces().items
        else:
            self.app_data = self.app_api.list_namespaced_daemon_set(namespace).items
        try:
            for content in self.app_data:
                name = content.metadata.name
                namespace = content.metadata.namespace
                labels = content.metadata.labels
                create_time = dt_format(content.metadata.creation_timestamp)
                number_ready = content.status.number_ready
                number_desired = content.status.desired_number_scheduled
                containers = content.spec.template.spec.containers[0].name
                data = {
                    'namespace': namespace,
                    'name': name,
                    'labels': labels,
                    'create_time': create_time,
                    'number_ready': number_ready,
                    'number_desired': number_desired,
                    'containers': containers,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.app_api.delete_namespaced_daemon_set(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        name = self.request.POST['name']
        namespace = self.request.POST['daemonset_namespace']
        # 检查daemonset是否存在
        for app in self.app_api.list_namespaced_daemon_set(namespace=namespace).items:
            if name == app.metadata.name:
                return {'code': 1, 'msg': 'daemonset is exits'}
        image = self.request.POST['image-name']
        limit = self.request.POST['limit']
        # 标签处理
        labels = self.labels_handle(self.request.POST['labels'].split(","))
        # try:
        #     for label in :
        #         k = label.split("=")[0]
        #         v = label.split("=")[1]
        #         labels[k] = v
        # except IndexError:
        #     res = {"code": 1, "msg": "标签格式错误，请修改！"}
        #     return res

        limit = self.set_limit(limit)

        body = client.V1DaemonSet(
            api_version="apps/v1",
            kind="DaemonSet",
            metadata=client.V1ObjectMeta(name=name),
            spec=client.V1DaemonSetSpec(
                selector={'matchLabels': labels},
                template=client.V1PodTemplateSpec(
                    metadata=client.V1ObjectMeta(labels=labels),
                    spec=client.V1PodSpec(
                        containers=[client.V1Container(
                            name=name,
                            image=image,
                            resources=limit,
                            image_pull_policy="IfNotPresent",
                        )]
                    )
                )
            )
        )
        # print(body)
        try:
            self.app_api.create_namespaced_daemon_set(namespace=namespace, body=body)
            code = 0
            msg = "创建成功"
        except Exception as e:
            return self.error_check(e)

        return {'code': code, 'msg': msg}


class StatefulsetApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.app_api = client.AppsV1Api()

    def get(self):
        contents = []
        namespace = self.request.GET.get('namespace')
        if namespace == 'all':
            app_data = self.app_api.list_stateful_set_for_all_namespaces().items
        else:
            app_data = self.app_api.list_namespaced_stateful_set(namespace).items

        try:
            for content in app_data:
                name = content.metadata.name
                namespace = content.metadata.namespace
                labels = content.spec.template.metadata.labels
                create_time = dt_format(content.metadata.creation_timestamp)
                containers = content.spec.template.spec.containers[0].name
                if content.status.ready_replicas is None:
                    ready_replicas = 0
                else:
                    ready_replicas = content.status.ready_replicas
                status = ('%s/%s' % (ready_replicas, content.status.replicas))

                data = {
                    'namespace': namespace,
                    'name': name,
                    'labels': labels,
                    'create_time': create_time,
                    'containers': containers,
                    'status': status,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.app_api.delete_namespaced_stateful_set(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        name = self.request.POST['name']
        namespace = self.request.POST['statefulset_namespace']
        # 检查deployment是否存在
        for app in self.app_api.list_namespaced_stateful_set(namespace=namespace).items:
            if name == app.metadata.name:
                return {'code': 1, 'msg': 'statefulset is exits'}
        # 标签处理
        labels = self.labels_handle(self.request.POST['labels'].split(","))
        image = self.request.POST['image-name']
        replica = int(self.request.POST['replica'])
        limit = self.request.POST['limit']

        limit = self.set_limit(limit)
        container = client.V1Container(
            name=name,
            image=image,
            image_pull_policy='IfNotPresent',
            resources=limit,
        )
        template = client.V1PodTemplateSpec(
            metadata=client.V1ObjectMeta(labels=labels),
            spec=client.V1PodSpec(containers=[container])
        )
        spec = client.V1StatefulSetSpec(
            replicas=replica,
            selector={'matchLabels': labels},
            template=template,
            service_name=name,
        )
        body = client.V1StatefulSet(
            api_version="apps/v1",
            kind="StatefulSet",
            metadata=client.V1ObjectMeta(name=name),
            spec=spec
        )

        try:
            self.app_api.create_namespaced_stateful_set(namespace=namespace, body=body)
            code = 0
            msg = "创建成功"
        except Exception as e:
            return self.error_check(e)

        return {'code': code, 'msg': msg}


class ContainerApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.app_api = client.CoreV1Api()

    def get(self):
        request_data = self.request.GET
        namespace = request_data['namespace']
        name = request_data['name']
        container_name = []
        try:
            for pod in self.app_api.list_namespaced_pod(namespace=namespace).items:
                if pod.metadata.name == name:
                    for container in pod.spec.containers:
                        container_name.append(container.name)
            code = 0
            msg = '获取container数据成功'
            contents = container_name
        except Exception as e:
            return self.error_check(e)

        res = {'code': code, 'msg': msg, 'data': contents}
        return res


class PodApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.app_api = client.CoreV1Api()

    def get(self):
        namespace = self.request.GET.get('namespace')
        if namespace == 'all' or namespace is None:
            app_data = self.app_api.list_pod_for_all_namespaces().items
        else:
            app_data = self.app_api.list_namespaced_pod(namespace).items
        contents = []
        try:
            for content in app_data:
                # print(content)
                pod_status = content.status.phase
                container_status = content.status.container_statuses
                if container_status is None:
                    container_number = 0
                else:
                    container_number = len(container_status)
                    container_ready_number = 0
                    restart_count = 0
                    for status in container_status:
                        if status.ready:
                            container_ready_number += 1
                            restart_count += status.restart_count
                        else:
                            if status.state.waiting is not None:
                                pod_status = status.state.waiting.reason
                            restart_count += status.restart_count
                name = content.metadata.name
                namespace = content.metadata.namespace
                # labels = content.spec.template.metadata.labels
                create_time = dt_format(content.metadata.creation_timestamp)
                image = content.spec.containers[0].image
                ready = ('%s/%s' % (container_ready_number, container_number))
                node = content.spec.node_name
                # 获取cpu和内存限制
                requests_cpu = 0
                requests_memory = 0
                limit_cpu = 0
                limit_memory = 0
                if content.spec.containers is not None:
                    for container in content.spec.containers:
                        if container.resources.requests is not None:
                            if 'cpu' in container.resources.requests and container.resources.requests['cpu'] is not None:
                                requests_cpu += cpu_format(container.resources.requests['cpu'])
                            if 'memory' in container.resources.requests and container.resources.requests['memory'] is not None:
                                requests_memory += memory_format(container.resources.requests['memory'])
                        if container.resources.limits is not None:
                            if 'cpu' in container.resources.limits and container.resources.limits['cpu'] is not None:
                                limit_cpu += cpu_format(container.resources.limits['cpu'])
                            if 'memory' in container.resources.limits and container.resources.limits['memory'] is not None:
                                limit_memory += memory_format(container.resources.limits['memory'])

                data = {
                    'namespace': namespace,
                    'name': name,
                    # 'labels': labels,
                    'create_time': create_time,
                    'image': image,
                    'status': pod_status,
                    'ready': ready,
                    'node': node,
                    'restart_count': restart_count,
                    'requests_cpu': requests_cpu,
                    'requests_memory': requests_memory,
                    'limit_cpu': limit_cpu,
                    'limit_memory': limit_memory
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)



        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.app_api.delete_namespaced_pod(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        name = self.request.POST['name']
        namespace = self.request.POST['pod_namespace']
        # 检查pod是否存在
        for app in self.app_api.list_namespaced_pod(namespace=namespace).items:
            if name == app.metadata.name:
                return {'code': 1, 'msg': 'pod is exits'}

        image = self.request.POST['image-name']
        limit = self.request.POST['limit']

        limit = self.set_limit(limit)
        container = client.V1Container(
            name=name,
            image=image,
            image_pull_policy='IfNotPresent',
            resources=limit,
        )
        spec = client.V1PodSpec(
            containers=[container]
        )
        body = client.V1Pod(
            # api_version="apps/v1",
            # kind="Pod",
            metadata=client.V1ObjectMeta(name=name),
            spec=spec
        )
        # print(body)
        try:
            self.app_api.create_namespaced_pod(namespace=namespace, body=body)
            code = 0
            msg = "创建成功"
        except Exception as e:
            return self.error_check(e)

        return {'code': code, 'msg': msg}


class ServiceApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.core_api = client.CoreV1Api()

    def get(self):
        namespace = self.request.GET.get('namespace')
        if namespace == 'all':
            app_data = self.core_api.list_service_for_all_namespaces().items
        else:
            app_data = self.core_api.list_namespaced_service(namespace=namespace).items
        contents = []
        try:
            for content in app_data:
                name = content.metadata.name
                namespace = content.metadata.namespace
                type = content.spec.type
                cluster_ip = content.spec.cluster_ip
                if content.spec.external_i_ps is None:
                    external_ip = 'None'
                else:
                    external_ip = content.spec.external_i_ps
                ports = []
                for app_port in content.spec.ports:
                    port = app_port.port
                    protocol = app_port.protocol
                    ports.append('%s/%s' % (port, protocol))
                create_time = dt_format(content.metadata.creation_timestamp)
                selector = content.spec.selector
                data = {
                    'name': name,
                    'namespace': namespace,
                    'type': type,
                    'cluster_ip': cluster_ip,
                    'external_ip': external_ip,
                    'ports': ports,
                    'create_time': create_time,
                    'labels': selector,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.core_api.delete_namespaced_service(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        name = self.request.POST['name']
        port = int(self.request.POST['port'])
        target_port = int(self.request.POST['target_port'])
        namespace = self.request.POST['services_namespace']
        # 检查service是否存在
        for app in self.core_api.list_namespaced_service(namespace=namespace).items:
            if name == app.metadata.name:
                return {'code': 1, 'msg': 'pod is exits'}
        # 标签处理
        labels = self.labels_handle(self.request.POST['labels'].split(","))

        spec = client.V1ServiceSpec(
            selector=labels,
            ports=[client.V1ServicePort(protocol="TCP", port=port, target_port=target_port)]
        )

        body = client.V1Service(
            api_version="v1",
            kind="Service",
            metadata=client.V1ObjectMeta(name=name),
            spec=spec
        )
        try:
            self.core_api.create_namespaced_service(namespace=namespace, body=body)
            code = 0
            msg = "创建成功"
        except Exception as e:
            return self.error_check(e)

        return {'code': code, 'msg': msg}


class IngressApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.network_api = client.NetworkingV1Api()

    def get(self):
        namespace = self.request.GET.get('namespace')
        if namespace == 'all':
            app_data = self.network_api.list_ingress_for_all_namespaces().items
        else:
            app_data = self.network_api.list_namespaced_ingress(namespace=namespace).items
        contents = []
        try:
            for content in app_data:
                name = content.metadata.name
                namespace = content.metadata.namespace
                if content.spec.ingress_class_name is None:
                    class_name = 'None'
                else:
                    class_name = content.spec.ingress_class_name
                host = []
                for h in content.spec.rules:
                    host.append(h.host)
                create_time = dt_format(content.metadata.creation_timestamp)
                data = {
                    'name': name,
                    'namespace': namespace,
                    'class_name': class_name,
                    'host': host,
                    'create_time': create_time,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.network_api.delete_namespaced_ingress(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        name = self.request.POST['name']
        host = self.request.POST['host']
        path = self.request.POST['path']
        path_type = self.request.POST['path_type']
        service = self.request.POST['service']
        port = int(self.request.POST['port'])
        namespace = self.request.POST['ingress_namespace']
        # 判读ingress是否存在
        for ingress in self.network_api.list_namespaced_ingress(namespace=namespace).items:
            if name in ingress.metadata.name:
                return {'code': 1, 'msg': 'ingress is exists'}

        body = client.V1Ingress(
            api_version="networking.k8s.io/v1",
            kind="Ingress",
            metadata=client.V1ObjectMeta(name=name, annotations={
                "nginx.ingress.kubernetes.io/rewrite-target": "/"
            }),
            spec=client.V1IngressSpec(
                rules=[client.V1IngressRule(
                    host=host,
                    http=client.V1HTTPIngressRuleValue(
                        paths=[client.V1HTTPIngressPath(
                            path=path,
                            path_type=path_type,
                            backend=client.V1IngressBackend(
                                service=client.V1IngressServiceBackend(
                                    port=client.V1ServiceBackendPort(
                                        number=port,
                                    ),
                                    name=service)
                            )
                        )]
                    )
                )
                ]
            )
        )
        try:
            self.network_api.create_namespaced_ingress(namespace=namespace, body=body)
            code = 0
            msg = "创建成功"
        except Exception as e:
            return self.error_check(e)

        return {'code': code, 'msg': msg}


class PersistentVolumeClaimsApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.core_api = client.CoreV1Api()

    def get(self):
        namespace = self.request.GET.get('namespace')
        if namespace == 'all':
            app_data = self.core_api.list_persistent_volume_claim_for_all_namespaces().items
        else:
            app_data = self.core_api.list_namespaced_persistent_volume_claim(namespace=namespace).items
        contents = []
        try:
            for content in app_data:
                name = content.metadata.name
                namespace = content.metadata.namespace
                status = content.status.phase
                volume = content.spec.volume_name
                if content.status.capacity is None:
                    capacity = 'None'
                else:
                    capacity = content.status.capacity['storage']
                access_modes = content.spec.access_modes
                storage_class = content.spec.storage_class_name
                create_time = dt_format(content.metadata.creation_timestamp)
                data = {
                    'name': name,
                    'namespace': namespace,
                    'status': status,
                    'volume': volume,
                    'capacity': capacity,
                    'access_modes': access_modes,
                    'storage_class': storage_class,
                    'create_time': create_time,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        print(contents)
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.core_api.delete_namespaced_persistent_volume_claim(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')


class ConfigmapApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.core_api = client.CoreV1Api()

    def get(self):
        namespace = self.request.GET.get('namespace')
        if namespace == 'all':
            app_data = self.core_api.list_config_map_for_all_namespaces().items
        else:
            app_data = self.core_api.list_namespaced_config_map(namespace=namespace).items
        contents = []
        try:
            for content in app_data:
                name = content.metadata.name
                namespace = content.metadata.namespace
                if content.data:
                    config_num = len(content.data)
                else:
                    config_num = 0
                create_time = dt_format(content.metadata.creation_timestamp)
                data = {
                    'name': name,
                    'namespace': namespace,
                    'config_num': config_num,
                    'create_time': create_time,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.core_api.delete_namespaced_config_map(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        all_key = self.request.POST.keys()
        name = self.request.POST['name']
        namespace = self.request.POST['configmap_namespace']
        body = client.V1ConfigMap()
        body.metadata = client.V1ObjectMeta(name=name)
        body.data = {}
        keys = 0
        for key in all_key:
            if 'data_key' in key:
                keys += 1
        for num in range(0, keys):
            key = 'data_key' + str(num)
            value = 'data_value' + str(num)
            body.data[self.request.POST[key]] = self.request.POST[value]

        try:
            self.core_api.create_namespaced_config_map(namespace=namespace, body=body)
            code = 0
            msg = "创建成功"
        except Exception as e:
            return self.error_check(e)
        return {'code': code, 'msg': msg}


class SecretsApi(K8sApi):
    def __init__(self, request):
        super().__init__(request)
        self.core_api = client.CoreV1Api()

    def get(self):
        namespace = self.request.GET.get('namespace')
        if namespace == 'all':
            app_data = self.core_api.list_secret_for_all_namespaces().items
        else:
            app_data = self.core_api.list_namespaced_secret(namespace=namespace).items
        contents = []
        try:
            for content in app_data:
                name = content.metadata.name
                namespace = content.metadata.namespace
                if content.data:
                    config_num = len(content.data)
                else:
                    config_num = 0
                create_time = dt_format(content.metadata.creation_timestamp)
                data = {
                    'name': name,
                    'namespace': namespace,
                    'config_num': config_num,
                    'create_time': create_time,
                }
                if self.search_key:
                    if self.search_key in name:
                        contents.append(data)
                else:
                    contents.append(data)
        except Exception as e:
            return self.error_check(e)

        code = 0
        msg = '数据获取成功'
        count = len(contents)
        contents = self.paging(contents)
        res = {'code': code, 'msg': msg, 'count': count, 'data': contents}
        return res

    def delete(self):
        request_data = QueryDict(self.request.body)
        name = request_data['name']
        namespace = request_data['namespace']
        try:
            self.core_api.delete_namespaced_secret(name=name, namespace=namespace)
            code = 0
            msg = '删除成功！'
            res = {'code': code, 'msg': msg}
            return res
        except Exception as e:
            return self.error_check(e, method='delete')

    def post(self):
        all_key = self.request.POST.keys()
        name = self.request.POST['name']
        namespace = self.request.POST['secrets_namespace']
        body = client.V1Secret()
        body.metadata = client.V1ObjectMeta(name=name, namespace=namespace)
        body.type = "Opaque"
        body.api_version = "v1"
        body.kind = "Secret"
        body.data = {}
        keys = 0
        for key in all_key:
            if 'data_key' in key:
                keys += 1
        for num in range(0, keys):
            key = 'data_key' + str(num)
            value = 'data_value' + str(num)
            # 因为base64只接受bytes内容，所以需要先转成bytes格式，之后再转换回str
            bytes_value = base64.b64encode(self.request.POST[value].encode('utf-8'))
            body.data[self.request.POST[key]] = (bytes_value.decode('utf-8'))
        try:
            self.core_api.create_namespaced_secret(namespace=namespace, body=body)
            code = 0
            msg = "创建成功"
        except Exception as e:
            return self.error_check(e)

        return {'code': code, 'msg': msg}


class ContainerLog(K8sApi):
    def __init__(self, request):
        super().__init__(request=request, request_type='websocket')
        self.core_api = client.CoreV1Api()

    def get_container_log(self, namespace, pod_name, container, tail_line=100):
        """
        获取容器日志
        tail_line 是显示最后多少行，默认为100
        """
        try:
            log_stream = self.core_api.read_namespaced_pod_log(
                name=pod_name,
                namespace=namespace,
                container=container,
                follow=True,
                pretty=True,
                _preload_content=False,
                timestamps=True,
                tail_lines=tail_line
            ).stream()
        except Exception as e:
            return self.error_check(e)
        return log_stream


class ContainerSSH(K8sApi):
    def __init__(self, request):
        super().__init__(request=request, request_type='websocket')
        self.core_api = client.CoreV1Api()

    def ssh_pod(self, namespace, pod_name, container, rows=24, cols=80):
        """
        获取容器日志
        tail_line 是显示最后多少行，默认为100
        """
        exec_command = [
            "/bin/sh",
            "-c",
            'TERM=xterm-256color; export TERM; [ -x /bin/bash ] '
            '&& ([ -x /usr/bin/script ] '
            '&& /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) '
            '|| exec /bin/sh']
        # print('namespace: %s, container: %s, name: %s' %(namespace, container, pod_name))
        try:
            ssh_stream = stream(self.core_api.connect_get_namespaced_pod_exec,
                                name=pod_name,
                                namespace=namespace,
                                command=exec_command,
                                container=container,
                                stderr=True, stdin=True,
                                stdout=True, tty=True,
                                _preload_content=False)
            ssh_stream.write_channel(4, json.dumps({"Height": int(rows), "Width": int(cols)}))
        except Exception as e:
            return self.error_check(e)
        return ssh_stream


class K8SLogStreamThread(threading.Thread):

    def __init__(self, websocket, container_stream):
        super(K8SLogStreamThread, self).__init__()
        self.websocket = websocket
        self.stream = container_stream

    def run(self):
        for s in self.stream:
            if s:
                self.websocket.send(bytes_data=s)
            else:
                self.websocket.close()


class K8SSSHStreamThread(threading.Thread):

    def __init__(self, websocket, container_stream):
        super(K8SSSHStreamThread, self).__init__()
        self.websocket = websocket
        self.stream = container_stream

    def run(self):
        try:
            while self.stream.is_open():
                # 读取标准输入
                if self.stream.peek_stdout():
                    stdout = self.stream.read_stdout()
                    self.websocket.send(stdout)
                # 读取标准输出
                if self.stream.peek_stderr():
                    stderr = self.stream.read_stderr()
                    self.websocket.send(stderr)
            else:
                self.websocket.close()
        except AttributeError as e:
            print('异常链接: %s' % e)
            return self.websocket.close()
